/**
 * Copyright (c) 2011 Tomi Paananen.
 *
 * Part of Quick Gestures user interface.
 */

// Own header
#include "gesturehelper.h"

// Qt includes
#include <QPoint>
#include <QStringList>

// qgengine includes
#include <featureextractor.h>
#include <gesture.h>
#include <gesturemodel.h>
#include <gestureprocessor.h>
#include <quickgesturesengine.h>
#include <qgcommon.h>
#include <storagethread.h>
#include <qgtrace.h>


/*!
  \class GestureHelper
  \brief A class containing helper methods for the UI.
*/


/*!
  Constructor.
*/
GestureHelper::GestureHelper(QObject *parent)
    : QObject(parent),
      m_featureExtractor(0),
      m_processor(0),
      m_autoProcess(false)
{
    m_featureExtractor = new FeatureExtractor(this);
    m_processor = new GestureProcessor();
}


/*!
  Destructor.
*/
GestureHelper::~GestureHelper()
{
    delete m_processor;
}


/*!
  Returns the number of modified gestures.
*/
int GestureHelper::modifiedGesturesCount() const
{
    return m_idsOfModifiedGestures.count();
}


/*!
  Returns true if automatic processing is enabled.
*/
bool GestureHelper::autoProcess() const
{
    return m_autoProcess;
}


/*!
  Enables/disables automatic processing according to \a autoProcess.
*/
void GestureHelper::setAutoProcess(bool autoProcess)
{
    if (autoProcess != m_autoProcess) {
        m_autoProcess = autoProcess;
        emit autoProcessChanged(m_autoProcess);
    }
}


/*!
  Copies the gesture data from \a src to \a dest. Returns true if successful,
  false otherwise.
*/
bool GestureHelper::copy(Gesture *src, Gesture *dest) const
{
    if (!src || !dest || src == dest /*|| *src == *dest*/) {
        return false;
    }

    *dest = *src;
    dest->setId(src->id());
    return true;
}


/*!
  Updates a gesture which has the same ID as \a gesture in \a model. Used if
  a copied gesture was modified and the original needs to be replaced with it.
  Returns true if successful, false otherwise.
*/
bool GestureHelper::update(Gesture *gesture, GestureModel *model) const
{
    if (!gesture || !model) {
        DEBUG_INFO("Invalid argument(s)!");
        return false;
    }

    if (m_autoProcess) {
        return process(gesture, model);
    }

    return model->addGesture(gesture);
}


/*!
  Makes a copy of \a gesture and appends it into \a model. Returns true if
  successful, false otherwise.
*/
bool GestureHelper::copyAndAppend(Gesture *gesture, GestureModel *model) const
{
    if (!gesture || !model) {
        DEBUG_INFO("Invalid argument(s): gesture ==" << gesture << "model ==" << model);
        return false;
    }

    Gesture *copy = new Gesture(*gesture);
    copy->setId(gesture->id());

    if (m_autoProcess) {
        return process(copy, model);
    }

    model->addGesture(copy);
    return true;
}


/*!
  Processes \a gesture and adds it into \a model. Returns true if successful,
  false otherwise.
*/
bool GestureHelper::process(Gesture *gesture, GestureModel *model) const
{
    if (!gesture || !model) {
        DEBUG_INFO("Invalid argument(s)!");
        return false;
    }

    if (m_featureExtractor->processGesture(gesture) !=
            QGEngine::NoError) {
        DEBUG_INFO("Failed to process gesture!");
        return false;
    }

    if (!model->addGesture(gesture)) {
        DEBUG_INFO("Failed to add the gesture into the given model!");
    }

    DEBUG_INFO("Successfully reprocessed a gesture with ID" << gesture->id());
    return true;
}


/*!
  Returns true if the list of IDs of modified gestures contains \a id, false
  otherwise.
*/
bool GestureHelper::isModified(quint32 id) const
{
    if (m_idsOfModifiedGestures.contains(id)) {
        return true;
    }

    return false;
}


/*!
  Extracts and returns the filename from \a path.
*/
QString GestureHelper::extractFilenameFromPath(const QString &path) const
{
    QStringList stringList = path.split('/', QString::SkipEmptyParts);
    DEBUG_INFO(path << "=>" << stringList.last());
    return stringList.last();
}


/*!
  Removes and returns the file from the end of \a path.
*/
QString GestureHelper::removeFileFromPath(const QString &path) const
{
    QStringList stringList = path.split('/', QString::SkipEmptyParts);

    if (stringList.count() && stringList.last().endsWith(".xml")) {
        stringList.removeLast();
    }

    QString joinedList;

    if (stringList.count() > 1) {
        joinedList = stringList.join("/");
    }

    DEBUG_INFO(path << "=>" << joinedList);
    return joinedList;
}


/*!
  Extracts and returns the local file as string from \a url.
*/
QString GestureHelper::urlToLocalFile(const QUrl &url) const
{
    DEBUG_INFO(url << "=>" << url.toLocalFile());
    return url.toLocalFile();
}


/*!
  Ensures that \a path is valid. If the given path is invalid, it will be
  corrected and then returned. Otherwise the original path will be returned.
  In addition, any excess whitespaces will be removed.
*/
QUrl GestureHelper::ensurePathValidity(const QUrl &path) const
{
    QString localFile = path.toString();
    QStringList stringList = localFile.split('/', QString::SkipEmptyParts);

    if (!stringList.last().endsWith(".xml")) {
        localFile += ".xml";
    }

    localFile = localFile.simplified();
    DEBUG_INFO(path << "=>" << QUrl(localFile));
    return QUrl(localFile);
}


/*!
  Appends \a id into the list of IDs of modified gestures.
*/
void GestureHelper::setModified(quint32 id)
{
    if (!m_idsOfModifiedGestures.contains(id)) {
        m_idsOfModifiedGestures.append(id);
        emit modifiedGesturesCountChanged(m_idsOfModifiedGestures.count());
    }

    // Always notify about modification
    emit modified(id);
}


/*!
  Clears the list of IDs.
*/
void GestureHelper::clearModifiedGestures()
{
    if (m_idsOfModifiedGestures.count()) {
        m_idsOfModifiedGestures.clear();
        emit modifiedGesturesCountChanged(0);
    }
}


/*!
  Saves the current set of \a engine to \a path.
*/
void GestureHelper::saveGestures(const QUrl &path, QuickGesturesEngine *engine)
{
    DEBUG_INFO(path);

    if (!engine) {
        return;
    }

    StorageThread *storageThread = engine->storageThread();

    if (!storageThread) {
        return;
    }

    QList<Gesture*> gestures = engine->gestureModel()->gestures();
    storageThread->saveByUrl(path, gestures);
}


/*!
  Centers the gesture on an area with \a width and \a height. The centering is
  done by manipulating the coordinates of the gesture points.
*/
void GestureHelper::center(Gesture *gesture, int width, int height)
{
    DEBUG_INFO(gesture << width << height);

    if (gesture && width > 1 && height > 1) {
        PointList points = gesture->modifiablePoints();

        if (points.count()) {
            m_processor->moveCenter(points, QPoint(width / 2, height / 2));
            gesture->setPoints(points);
        }
    }
}


// End of file.
